/*
 * marketsim-eclipsetrader
 *
 * Copyright (C) 2008-2011 Steve Phelps and John Cartlidge
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 */
package victria.marketsim.core;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

import org.apache.log4j.Logger;

public class Order implements Cloneable, Serializable {
	
	static Logger logger = Logger.getLogger(Order.class);

	public enum Direction { BUY, SELL };
	
	protected int id;
	
	protected Object trader;
	
    protected Date date = Calendar.getInstance().getTime();     
    
    protected int quantity;
    
    protected float price;
    
    protected float stopPrice;
    
    protected int filledQuantity;    
    
    protected Date expire;
    
    protected Direction direction;
    
    protected boolean visible = true;
    
    protected int minimumOrderSize;
    
    protected int maximumOrderSize;
    
    public Order() {
    	
    	//Orders are initialised to default
    	logger.debug("Creating new order " + this);
    	minimumOrderSize = 0;
    	maximumOrderSize = Integer.MAX_VALUE; 
    	expire = null;
    }
    
    public boolean isBid() {
    	return direction == Direction.BUY;
    }
    
    public void setIsBid(boolean isBid) {
    	if (isBid) {
    		direction = Direction.BUY;
    	} else {
    		direction = Direction.SELL;
    	}
    }
    
    public boolean isAsk() {
    	return direction == Direction.SELL;
    }
    
	public boolean isVisible() {
		return visible;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}
	
	public boolean isExpired() {
		if (expire == null) {
			return false;
		} else {
			Date now = new Date();
			return (now.after(expire));
		}
	}
	
	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	
	public Date getExpire() {
		return expire;
	}

	public void setExpire(Date expire) {
		this.expire = expire;
	}

	public int getFilledQuantity() {
		return filledQuantity;
	}

	public void setFilledQuantity(int filledQuantity) {
		this.filledQuantity = filledQuantity;
	}

	public float getPrice() {
		return price;
	}

	public void setPrice(float price) {
		this.price = price;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
		if(quantity <= maximumOrderSize) {
			maximumOrderSize = quantity;
		}
		if(quantity <= minimumOrderSize) {
			minimumOrderSize = quantity;
		}
	}

	public float getStopPrice() {
		return stopPrice;
	}

	public void setStopPrice(float stopPrice) {
		this.stopPrice = stopPrice;
	}
	
	public Direction getDirection() {
		return direction;
	}

	public void setDirection(Direction direction) {
		this.direction = direction;
	}

	public long getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Object getTrader() {
		return trader;
	}

	public void setTrader(Object trader) {
		this.trader = trader;
	}
		
	public boolean matches(Order other) {
		if (this.getTrader() == other.getTrader()
				|| (this.getTrader() != null && this.getTrader().equals(
						other.getTrader()))) {
			return false;
		} else if (this.getMinimumOrderSize() <= other.getMaximumOrderSize()
				&& this.getMaximumOrderSize() >= other.getMinimumOrderSize()) {

			return (this.isBid() && other.isAsk() && this.getPrice() >= other.getPrice())
						|| (this.isAsk() && other.isBid() && other.getPrice() >= this.getPrice());
			
		} else {
			return false;
		}
	}
	
	public boolean equals(Order other) {
		return this.id == other.id;
	}
	
	/**
	 * Return true if orders posted by the same trader
	 */
	public boolean postedBySameTraderAs(Order other) {
		if(this.getTrader().equals(other.getTrader())) {
			return true;
		} else {
			return false;
		}
	}
	
	public boolean isMatchable() {
		return true;
	}
	
	public int hashCode() {
		return (int) id;
	}
	
	
	public int getMinimumOrderSize() {
		if(minimumOrderSize > quantity) {
			minimumOrderSize = quantity;
		}
		return minimumOrderSize;
	}
	
	public void setMinimumOrderSize(int minimumOrderSize) {
		this.minimumOrderSize = minimumOrderSize;
	}
	
	/** Return maximumOrderSize.
	 *  Clips maximumOrderSize to quantity 
	 * @return
	 */
	public int getMaximumOrderSize() {
		if(maximumOrderSize > quantity) {
			maximumOrderSize = quantity;
		}
		return maximumOrderSize;
	}
	
	public void setMaximumOrderSize(int maximumOrderSize) {
		this.maximumOrderSize = maximumOrderSize;
	}
	
	/**
	 * Return the transaction price when two orders execute
	 * That is, the price of the oldest order
	 */
	public float getTransactionPrice(Order other) {		
		if (this.getDate().compareTo(other.getDate()) < 0) {
			return this.getPrice();
		} else {
			return other.getPrice();
		}
	}
	
	/**
	 * Return the maximum single transaction size when two orders execute
	 * That is, the smallest maximumOrderSize
	 */
	public int getMaximumSingleTransactionSize(Order other) {
		if(other.getMaximumOrderSize() <= this.getMaximumOrderSize()) {
			return other.getMaximumOrderSize();
		} else {
			return this.getMaximumOrderSize();
		}
	}
	
	/**
	 * Return the fulfilled quantity when two orders execute.
	 * That is, the minimum quantity of two orders
	 */
	public int getFulfilledQuantity(Order other) {
		if (this.getOutstandingQuantity() <= other.getOutstandingQuantity()) {
			return this.getOutstandingQuantity(); 
		} else {
			return other.getOutstandingQuantity();
		}
	}

	public void increaseFilledQuantity(int filled) {
		filledQuantity += filled;
	}
	
	public int getOutstandingQuantity() {
		return quantity - filledQuantity;
	}
	
	public Object clone() throws CloneNotSupportedException {
		Order result = (Order) super.clone();
		result.id = -1;
		return result;
	}

	public String toPrettyString() {
		return "[" + getTrader() + " " + getPrice() + "/" + getQuantity() + " " + getDirection() + "]";
	}
	
    public String toString() {
		return "(" + getClass() + " id:" + id + " trader:" + trader + " price:"
				+ price + " quantity:" + quantity + " direction:" + direction
				+ " visible:" + visible + " minimumOrderSize:" + minimumOrderSize
				+ " maximumOrderSize:" + maximumOrderSize + " expire:" + expire 
				+ " uuiMappingParameters:"+ ")";
	}


}